/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package Operations.Wavelet;

import JSci.maths.wavelet.*;
import JSci.maths.wavelet.daubechies2.Daubechies2;
import java.awt.Dimension;

/**
 *
 * @author littlechild
 */
public class Wavelet {
    Daubechies2 filter=new Daubechies2();
    public int getFilterType() {
        return filter.getFilterType();
    }
    double[][] transform(double[][] ori,int level,Dimension LLsize) {
        int widthped=getpedding(ori.length, filter.getFilterType()/2,level);
        int heightped=getpedding(ori[0].length, filter.getFilterType()/2,level);
        double [][]transformed=new double[ori.length+widthped][ori[0].length+heightped];
        for(int i=0;i!=transformed.length;++i) {
            if(i<ori.length) {
                System.arraycopy(ori[i], 0, transformed[i], 0, ori[0].length);
                for(int j=0;j!=heightped;++j) {
                    transformed[i][ori[0].length+j]=ori[i][ori[0].length-1];
                }
            }
            else {
                System.arraycopy(transformed[ori.length-1], 0, transformed[i], 0, transformed[0].length);
            }
        }
        //transform for lines
        int width=transformed.length;
        int height=transformed[0].length;
        for(int i=0;i!=level;++i) {
            transformOnRow(transformed,width,height);
            transformOnColumn(transformed,width,height);
            width=width/2+filter.getFilterType()/2;
            height=height/2+filter.getFilterType()/2;
            reduceEnergy(transformed,width,height);
        }
        LLsize.setSize(width, height);
        //tranform for columns
        return transformed;
    }
    double[][] invTransform(double[][] com,int level) {
        int width=com.length;
        int height=com[0].length;
        double transformed[][]=new double[com.length][com[0].length];
        for(int i=0;i!=com.length;++i) {
            System.arraycopy(com[i], 0, transformed[i], 0, com[0].length);
        }
        for(int i=0;i!=level;++i) {
            width=width/2+filter.getFilterType()/2;
            height=height/2+filter.getFilterType()/2;
        }
        for(int l=0;l!=level;++l) {
            increaseEnergy(transformed,width,height);
            invTransformOnColumn(transformed,width,height);
            invTransformOnRow(transformed,width,height);
            width=(width-filter.getFilterType()/2)*2;
            height=(height-filter.getFilterType()/2)*2;
        }
        return transformed;
    }

    private void invTransformOnColumn(double[][] com, int width, int height) {
        int regionWidth=(width-filter.getFilterType()/2)*2;
        int regionHeight=(height-filter.getFilterType()/2)*2;
        for(int column=0;column<regionWidth;++column) {
            double low[]=new double[height];
            System.arraycopy(com[column],0,low,0,height);
            double high[]=new double[regionHeight-height];
            System.arraycopy(com[column],height,high,0,regionHeight-height);
            double [][]tmp=new double [2][];
            tmp[0]=low;
            tmp[1]=high;
            FWTCoef sCoef=new FWTCoef(tmp);
            double []signal=sCoef.rebuildSignal(filter).evaluate(0);
            System.arraycopy(signal, 0, com[column], 0, regionHeight);
        }
    }

    private void invTransformOnRow(double[][] com, int width, int height) {
        double tmp[]=new double[width];
        int regionWidth=(width-filter.getFilterType()/2)*2;
        int regionHeight=(height-filter.getFilterType()/2)*2;
        double low[]=new double[width];
        double high[]=new double[regionWidth-width];
        for(int line=0;line<regionHeight;++line) {
            for(int column=0;column<width;++column) {
                low[column]=com[column][line];
            }
            for(int column=0;column!=regionWidth-width;++column) {
                high[column]=com[column+width][line];
            }
            FWTCoef sCoef=new FWTCoef(new double[2][]);
            sCoef.getCoefs()[0]=low;
            sCoef.getCoefs()[1]=high;
            double []signal=sCoef.rebuildSignal(filter).evaluate(0);
            for(int column=0;column!=regionWidth;++column) {
                com[column][line]=signal[column];
            }
        }
    }

    private void transformOnColumn(double [][]transformed,int width,int height) {
        for(int column=0;column<width;++column) {
            double tmp[]=new double[height];
            System.arraycopy(transformed[column],0,tmp,0,height);
            Signal signal=new Signal(tmp);
            signal.setFilter(filter);
            FWTCoef sCoef=signal.fwt(1);
            System.arraycopy(sCoef.getCoefs()[0], 0,transformed[column] , 0, sCoef.getCoefs()[0].length);
            System.arraycopy(sCoef.getCoefs()[1], 0,transformed[column] , sCoef.getCoefs()[0].length, sCoef.getCoefs()[1].length);
        }
    }

    private void transformOnRow(double [][]transformed,int width,int height) {
        double tmp[]=new double[width];
        for(int line=0;line<height;++line) {
            for(int column=0;column<width;++column) {
                tmp[column]=transformed[column][line];
            }
            Signal signal=new Signal(tmp);
            signal.setFilter(filter);
            FWTCoef sCoef=signal.fwt(1);
            int column=0;
            while(column<sCoef.getCoefs()[0].length) {
                transformed[column][line]=sCoef.getCoefs()[0][column];
                column++;
            }
            while(column<width) {
                transformed[column][line]=sCoef.getCoefs()[1][column-sCoef.getCoefs()[0].length];
                column++;
            }
        }
    }
    private int getpedding(int length,int filterType,int level) {
        //((x/2+3) /2 +3 )/2 +3
        for(int i=0;true;++i) {
            int tmp=length+i;
            int j;
            for(j=0;j!=level;++j) {
                if(tmp%2!=0)
                    break;
                tmp=tmp/2+filterType;
            }
            if(j==level)
                return i;
        }
    }

    private void reduceEnergy(double[][] transformed, int width, int height) {
        for(int i=0;i!=width;++i) {
            for(int j=0;j!=height;++j) {
                transformed[i][j]/=2;
            }
        }
    }
    private void increaseEnergy(double[][] transformed, int width, int height) {
        for(int i=0;i!=width;++i) {
            for(int j=0;j!=height;++j) {
                transformed[i][j]*=2;
            }
        }
    }
}
